﻿using System;
using ErgoBreak.Data;

namespace ErgoBreak.Core
{
    internal sealed class Controller : IController, IUpdatableController
    {
        #region Fields
        private IBreakManager manager;
        private ObserverBase observer;
        private BreakActionFactory baFactory;
        #endregion

        #region Events
        /// <summary>
        /// Happens when a break is started.
        /// </summary>
        public event EventHandler BreakStarted;

        /// <summary>
        /// Happens when a break is ended.
        /// </summary>
        public event EventHandler BreakEnded;

        /// <summary>
        /// Occurs when a break is canceled.
        /// </summary>
        public event EventHandler BreakCanceled;

        /// <summary>
        /// Occurs when a break is snoozed. 
        /// </summary>
        public event EventHandler Snoozed;

        /// <summary>
        /// Occurs when a break snooze is over
        /// </summary>
        public event EventHandler SnoozeElapsed;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes an instance of 'Controller' class.
        /// </summary>
        /// <param name="breakManager">The instance of 'BreakManager' class.</param>
        /// <param name="observer"></param>
        /// <param name="breakActionFactory"></param>
        public Controller(IBreakManager breakManager, ObserverBase observer, BreakActionFactory breakActionFactory)
        {
            // initializations
            this.manager = breakManager;
            this.observer = observer;
            this.baFactory = breakActionFactory;
            
            // wire up event handlers
            this.observer.BreakProposed += new BreakEventHandler(ManageBreakSignal);
            this.manager.BreakStarted += new EventHandler(ManageBreakStart);
            this.manager.BreakEnded += new EventHandler(ManageBreakOver);
            this.manager.BreakCanceled += new EventHandler(ManageBreakCancelation);
            this.manager.Snoozed += new EventHandler(ManageSnoozeStart);
            this.manager.SnoozeElapsed += new EventHandler(ManageSnoozeOver);
        }
        #endregion 

        #region Public Methods
        /// <summary>
        /// Updates the internal observer with the given one.
        /// </summary>
        /// <param name="observer">The new observer object to replced with.</param>
        public void UpdateObserver(ObserverBase observer)
        {
            if (observer == null)
            {
                string msg = string.Format(Messages.GetMessage("Err202"), "obsrever");
                throw new NullReferenceException(msg);
            }

            this.observer = observer;
        }

        /// <summary>
        /// Cancels the ongoing break.
        /// </summary>
        public void CancelBreak()
        {
            this.manager.CancelBreak();
        }

        /// <summary>
        /// Postpones the current break.
        /// </summary>
        /// <param name="duration">The amount of time, in minutes, that it will postpone 
        /// the break for.</param>
        public void Postpone(int duration)
        {
            this.manager.Snooze(duration);
        }
        #endregion

        #region Non-Public Methods
        private void ManageBreakSignal(object sender, BreakEventArgs e)
        {
            // transfer the signal to the BreakManager
            this.manager.StartBreak(e.Break, baFactory.CreateAction(e.Break));
        }

        private void ManageSnoozeOver(object sender, EventArgs e)
        {
            // raise the event
            if (this.SnoozeElapsed != null)
                SnoozeElapsed(this, e);
        }

        private void ManageSnoozeStart(object sender, EventArgs e)
        {
            // raise the event
            if (this.Snoozed != null)
                Snoozed(this, e);
        }

        private void ManageBreakCancelation(object sender, EventArgs e)
        {
            // raise the event
            if (this.BreakCanceled != null)
                BreakCanceled(this, e);
        }

        private void ManageBreakOver(object sender, EventArgs e)
        {
            // raise the event
            if (this.BreakEnded != null)
                BreakEnded(this, e);
        }

        private void ManageBreakStart(object sender, EventArgs e)
        {
            // raise the event
            if (this.BreakStarted != null)
                BreakStarted(this, e);
        }
        #endregion
    }
}
